package gauss;

import jsr166y.ForkJoinPool;
import jsr166y.RecursiveAction;

public class ForkJoinFastGauss extends RecursiveAction {

	private static final long serialVersionUID = 1L;
	private double[][] source;
	private double[][] destination;
	private int startColumn;
	private int columnsToCompute;
	private int maskSum;
	private double[][] mask;

	public ForkJoinFastGauss(double[][] source, double[][] mask,int maskSum, int startColumn, int columnsToCompute, double[][] destination) {
		this.source = source;
		this.destination = destination;
		this.startColumn = startColumn;
		this.columnsToCompute = columnsToCompute;
		this.mask = mask;
		this.maskSum = maskSum;
	}

	private static void filterGauss(double[][] sourceMap, double[][] mask,int maskSum, int startColumn, int columnsToCompute,
		double[][] destinationMap) {

		int xDim = sourceMap.length;
		int yDim = sourceMap[0].length;
		int maskRadius = (mask.length - 1) / 2;
		
		for (int x = 0; x < destinationMap.length; x++) {
			for (int y = startColumn; y < startColumn + columnsToCompute; y++) {
				for (int mx = -maskRadius; mx <= maskRadius; mx++) {
					for (int my = -maskRadius; my <= maskRadius; my++) {
						if ((x + mx >= 0) && (y + my >= 0) && (x + mx < xDim) && (y + my < yDim)) {
							destinationMap[x][y] += (sourceMap[x + mx][y + my] * mask[mx + maskRadius][my + maskRadius])
									/ maskSum;
						}
					}
				}
			}
		}

	}

	protected void computeDirectly() {
		filterGauss(source,mask,maskSum, startColumn, columnsToCompute, destination);
	}

	protected static int sThreshold = 600/12;

	@Override
	protected void compute() {
		if (columnsToCompute <= sThreshold) {
			computeDirectly();
			return;
		}

		int split = columnsToCompute / 2;

		invokeAll(new ForkJoinFastGauss(source,mask,maskSum, startColumn, split, destination), new ForkJoinFastGauss(source,mask,maskSum,
				startColumn + split, columnsToCompute - split, destination));

	}

	public static double[][] forkJoinFastGauss(double[][] sourceMap) {
		int xDim = sourceMap.length;
		int yDim = sourceMap[0].length;
		double[][] destinationMap = new double[xDim][yDim];
		double[][] mask = { { 1, 2, 1 }, { 2, 4, 2 }, { 1, 2, 1 } };
		int maskSum = 0;
		for (int i = 0; i < mask.length; i++) {
			for (int j = 0; j < mask[i].length; j++) {
				maskSum += mask[i][j];
			}
		}
		
		ForkJoinFastGauss forkJoinFastMedian = new ForkJoinFastGauss(sourceMap,mask,maskSum, 0, yDim, destinationMap);
		ForkJoinPool pool = new ForkJoinPool();
		pool.invoke(forkJoinFastMedian);
		return (destinationMap);
	}

}
